#[macro_use]
extern crate log;

use std::{
    env,
    error::Error,
    fs::File,
    io::{BufRead, BufReader, Write},
    time::{Duration, Instant},
};

use chrono::{DateTime, NaiveDateTime, Timelike};
use env_logger::Builder;
use log::LevelFilter;
use reqwest::Response;
use tokio::{self, runtime::Runtime, sync};

mod utils;

#[derive(Debug)]
struct SendInfo {
    pub index: usize,
    pub time_elapsed: i64,
    pub timestamp: String,
    pub content: String,
}

pub async fn get_jenkins_log(job_name: &str, job_number: &str) -> Result<String, Box<dyn Error>> {
    let client = reqwest::Client::new();
    let url = format!(
        "http://jenkins2.biubiubiu.io:8080/job/{}/{}/consoleText",
        job_name, job_number
    );

    let response = get_response(&client, &url).await?;
    if response.status().as_u16() == 200 {
        let response = response.text().await?;
        return Ok(response);
    }

    return Err("".into());
}

pub async fn get_response(client: &reqwest::Client, url: &str) -> Result<Response, reqwest::Error> {
    let response = client
        .get(url)
        .basic_auth("linjunda", Some("11949af7c7407442508d2942dfabe5e784"))
        .timeout(Duration::from_secs(10))
        .send()
        .await?;

    Ok(response)
}

fn main() -> Result<(), Box<dyn Error>> {
    if true {
        let _ = utils::unity_log();
        return Ok(());
    }

    const FILTER_DELTA_TIME: i64 = 4000; // 定义了一个 i32 类型的常量

    let filter_git = false;
    let mut sort = false;
    let mut job_name = String::new();
    let mut job_number = String::new();
    let mut path = "E:\\Work\\Sausage\\sh\\consoleText_1711.txt";//"";

    let args = env::args().collect::<Vec<String>>();
    args.iter().enumerate().for_each(|(index, arg)| {
        if arg == "-sort" {
            sort = true;
        } else if arg == "-path" {
            path = &args[index + 1];
        } else if arg == "-job" {
            job_name = String::from(&args[index + 1]);
        } else if arg == "-number" {
            job_number = String::from(&args[index + 1]);
        }
    });

    //env_logger::init();
    let mut builder = Builder::from_default_env();
    builder
        .format(|buf, record| writeln!(buf, "{} - {}", record.level(), record.args()))
        .filter(None, LevelFilter::Info)
        .init();

    let rt = Runtime::new().unwrap();
    let mut list_cost = Vec::new();
    rt.block_on(async {
        let start = Instant::now();

        let mut lines_log: Vec<String> = Vec::new();
        if !path.is_empty() {
            let input = File::open(path).expect("Error : File::open");
            let lines = BufReader::new(input).lines();
            for line in lines {
                let Ok(cur_line) = line else {
                    continue;
                };

                lines_log.push(cur_line);
            }
            trace!("Tip : log len = {}", lines_log.len());
        } else {
            let Ok(mut response) = get_jenkins_log(&job_name, &job_number).await else {
                error!("获取不到 log");
                return;
            };
            if let Some(_) = response.find("\\r\\n") {
                response = response.replace("\r\n", "\n");
            }

            response.split('\n').into_iter().for_each(|v| {
                lines_log.push(String::from(v));
            });
            trace!("Tip : log len = {}", lines_log.len());
        }

        let lines_log_clone = lines_log.clone();
        println!("Tip : read file cost time = {:?}", start.elapsed());

        if lines_log.is_empty() {
            return;
        }

        let start = Instant::now();
        let mut pre_line = String::from(&lines_log[0]);
        let mut count = 0;
        let (tx, mut rx) = sync::mpsc::channel::<SendInfo>(lines_log.len());
        for line in lines_log {
            trace!("{}", line);
            if count == 0 {
                count += 1;
                continue;
            }

            let index = count;
            let pre_clone = String::from(&pre_line);
            let cur_clone = String::from(&line);
            let tx_clone: sync::mpsc::Sender<SendInfo> = tx.clone();
            tokio::spawn(async move {
                // [2023-08-01T04:30:25.606Z] +++ echo 'JenkinsTimer Shell_Build_AssetBundle 客户端接口CmdBuildAssetBundles ---> 22分2 秒'
                if let Some(_) = cur_clone.find("echo 'JenkinsTimer ") {
                    //  ---> 22分2 秒'
                    let l_time_find = cur_clone.rfind("---> ").unwrap() + "---> ".len();
                    let min_time_find = cur_clone.rfind("分").unwrap();
                    let sec_time_find = cur_clone.rfind("秒").unwrap();
                    let min = cur_clone[l_time_find..min_time_find]
                        .parse::<i64>()
                        .unwrap()
                        * 60
                        * 1000;
                    let sec = cur_clone[(min_time_find + "分".len())..(sec_time_find - 1)]
                        .parse::<i64>()
                        .unwrap()
                        * 1000;
                    let send_info = SendInfo {
                        index: index,
                        time_elapsed: min + sec,
                        content: cur_clone[27..].to_string(),
                        timestamp: cur_clone[1..24].to_string(),
                    };

                    if tx_clone.send(send_info).await.is_err() {
                        eprintln!("    Error[copy] : create_dir_all send error");
                    }
                    return;
                }

                // [2023-10-24T02:50:01.418Z] JenkinsTimer Shell：更新Library 切分支-checkout_client-BuildTime-2分42秒-Timer-10:47:17~10:49:59
                // [2023-08-01T04:07:33.004Z] JenkinsTimer 打包耗时：ClearCache ---> 1.8204097
                if false /*let Some(_) = cur_clone.find("JenkinsTimer ")*/ {
                    // ---> 0.0300069
                    let l_time_find = cur_clone.rfind("--->").unwrap() + "---> ".len();
                    let sec = (cur_clone[l_time_find..].parse::<f64>().unwrap() * 1000.0) as i64;
                    let send_info = SendInfo {
                        index: index,
                        time_elapsed: sec,
                        content: cur_clone[27..].to_string(),
                        timestamp: cur_clone[1..24].to_string(),
                    };

                    if tx_clone.send(send_info).await.is_err() {
                        eprintln!("    Error[copy] : create_dir_all send error");
                    }
                    return;
                }

                if filter_git {
                    let Some(_) = pre_clone.find("+++") else {
                        return;
                    };
                }

                if pre_clone.len() < 1 {
                    // ??
                    return;
                }

                let pre_clone_r = pre_clone.find(']');
                if &pre_clone[..1] != "[" || pre_clone_r.is_none() {
                    return;
                }

                if cur_clone.len() < 1 {
                    // ??
                    return;
                }

                trace!("cur_clone({}) = {}", index, cur_clone);
                let cur_clone_r = cur_clone.find(']');
                if &cur_clone[..1] != "[" || cur_clone_r.is_none() {
                    return;
                }

                let pre_clone_r = pre_clone_r.unwrap_or(0);
                trace!("caps_pre({}) = {}", index, &pre_clone[1..pre_clone_r]);
                let Ok(caps_pre) = DateTime::parse_from_rfc3339(&pre_clone[1..pre_clone_r]) else {
                    return;
                };

                trace!("caps_pre({}) = {}", index, caps_pre.timestamp());

                let cur_clone_r = cur_clone_r.unwrap_or(0);
                trace!("caps_cur({}) = {}", index, &cur_clone[1..cur_clone_r]);
                let Ok(caps_cur) = DateTime::parse_from_rfc3339(&cur_clone[1..cur_clone_r]) else {
                    return;
                };

                trace!("caps_cur({}) = {}", index, caps_cur.timestamp());
                debug!(
                    "deltaTime({}) = {}",
                    index,
                    caps_cur.timestamp_millis() - caps_pre.timestamp_millis()
                );

                let send_info = SendInfo {
                    index: index,
                    time_elapsed: caps_cur.timestamp_millis() - caps_pre.timestamp_millis(),
                    content: pre_clone[(pre_clone_r + 2)..].to_string(),
                    timestamp: pre_clone[1..pre_clone_r].to_string(),
                };

                if tx_clone.send(send_info).await.is_err() {
                    eprintln!("    Error[copy] : create_dir_all send error");
                }
            });
            pre_line = String::from(&line);
            count += 1;
        }

        drop(tx);

        while let Some(send_info) = rx.recv().await {
            if send_info.time_elapsed < FILTER_DELTA_TIME {
                continue;
            }

            let ts = NaiveDateTime::from_timestamp_millis(send_info.time_elapsed).unwrap();
            let mut tag = String::new();
            if let Some(_) = send_info.content.find(" git ") {
                let mut find = send_info.index - 1;
                loop {
                    // [2023-08-07T12:21:44.636Z] +++ cd D:/data/jenkins/workspace/client_standalone3/assets/dev_standalone
                    if let (Some(_), Some(r_split)) = (
                        lines_log_clone[find].find(" cd "),
                        lines_log_clone[find].rfind("/"),
                    ) {
                        tag = String::from(&lines_log_clone[find][(r_split + 1)..]);
                        break;
                    }

                    find -= 1;
                }
            }

            let content;
            if !tag.is_empty() {
                content = format!("[{}] {}", tag, send_info.content);
            } else {
                content = send_info.content;
            }

            let log_info = format!(
                "[{}] 耗时 {} ({}:{}:{}) - {}",
                send_info.index,
                send_info.time_elapsed,
                ts.hour(),
                ts.minute(),
                ts.second(),
                content
            );

            list_cost.push((send_info.index, send_info.time_elapsed, log_info));
        }

        if sort {
            list_cost.sort_by(|a, b| b.1.cmp(&a.1));
        } else {
            list_cost.sort_by(|a, b| a.0.cmp(&b.0));
        }

        for cost in list_cost {
            info!("{}", cost.2);
        }

        println!("Tip : cal cost time = {:?}", start.elapsed());
    });

    Ok(())
}
